#include <libnetwork/share.h>

#define SHARE_MAJ 1
#define SHARE_MIN 3
#define SHARE_SUB 7
#define SHARE_BUILD 12122008

// Internal function definitions
int _string_length(char *);
bool _share_version_check(unsigned int, unsigned int, unsigned int, unsigned int);
void gerr(char *);
// End internal function definitions

void err(message, socket)
char *message;
SOCK *socket;
{
 int _x;

 memset(&socket->_error_message, '\0', sizeof(char) * 255);

 for( _x = 0; message[_x] != '\0'; _x++ ) {
  socket->_error_message[_x] = message[_x];
 }
 socket->_error_message[_x] = '\0';

}

void print_err(socket)
SOCK *socket;
{
 fprintf(stderr, "%s\n", socket->_error_message);
 fflush(stderr);

 if( socket->_status&ERROR_UNRECOVERABLE ) {
  fprintf(stderr, "Error is unrecoverable, exiting\n");
  fflush(stderr);
  if( socket->_status&STATUS_PTHREAD ) {
   pthread_exit(NULL);
  } else {
   exit(1);
  }
 }
}

void gerr(message)
char *message;
{
 fprintf(stderr, "%s\n", message);
 fflush(stderr);

 exit(1);
}

int share_send(socket, message)
SOCK *socket;
char *message;
{
 int _size;

 if( !(socket->_status&STATUS_INIT) || !(socket->_status&STATUS_CONN) ) {
  err("Error: *.a->share.c->share_send(): Socket is not initialized", socket);
  socket->_status += STATUS_WRITE_ERROR;
  socket->_status += ERROR_UNRECOVERABLE;
  return -1;
 }

 _size = _string_length(message);

 if( socket->_status&STATUS_ENC ) {
  if( SSL_write( socket->_ssl, message, _size) <= 0 ) {
   err("Error: *.a->share.c->share_send(): failed to SSL_write()", socket);
   socket->_status += STATUS_WRITE_ERROR;
   return -1;
  }
 } else {
  if(send( socket->_sockfd, message, _size, 0) == -1) {
   err("Error: *.a->share.c->share_send(): failed to send()", socket);
   socket->_status += STATUS_WRITE_ERROR;
   return -1;
  }
 }
 return _size;
}

char *share_read(socket, max_read)
SOCK *socket;
int max_read;
{
 char _message[max_read], *_export_message;
 int _x;

 memset(&_message, '\0', max_read);

 if( !(socket->_status&STATUS_INIT) || !(socket->_status&STATUS_CONN) ) {
  err("Error: *.a->share.c->share_read(): Socket is not initialized", socket);
  socket->_status += STATUS_READ_ERROR;
  socket->_status += ERROR_UNRECOVERABLE;
  return NULL;
 }

 _x = recv(socket->_sockfd, &_message, max_read, MSG_PEEK | MSG_DONTWAIT);

 if( _x == 0 ) {
  socket->_status -= STATUS_CONN;
  return NULL;
 } else if( _x == -1) {
  return NULL;
 }

 if( socket->_status&STATUS_ENC ) {
  _x = SSL_read(socket->_ssl, &_message, max_read);
  if( _x <= 0 ) {
   err("Error: *.a->share.c->share_read(): failed to SSL_read()", socket);
   socket->_status += STATUS_READ_ERROR;
   return NULL;
  }
 } else {
  _x = recv(socket->_sockfd, &_message, max_read, 0);
  if( _x <= 0 ) {
   err("Error: *.a->share.c->share_read(): failed to recv()", socket);
   socket->_status += STATUS_READ_ERROR;
   return NULL;
  } 
 }

 _export_message = malloc(sizeof(char) * (_x + 1));
 memcpy(_export_message, &_message, (_x));
 _export_message[_x + 1] = '\0';

 return _export_message;
}

int _string_length(message)
char *message;
{
 int _x;

 for( _x = 0; message[_x] != '\0'; _x++ ) {
 }

 _x++;
 return _x;
}

int share_close_gracefully(socket)
SOCK *socket;
{
 if( !(socket->_status&STATUS_MAIN_SRV) && (socket->_status&STATUS_CONN) ) {
  share_send(socket, "Bye \\(^.^)/");
 }

 if( socket->_status&STATUS_ENC ) {
  if( !(socket->_status&STATUS_MAIN_SRV) ) {
   SSL_shutdown(socket->_ssl);
   SSL_free(socket->_ssl);
   SSL_CTX_free(socket->_ssl_ctx);
  }
  close(socket->_sockfd);
 } else {
  close(socket->_sockfd);  
 }
 _init_socket_variable(socket);

 return 0;
}

void _init_socket_variable(socket)
SOCK *socket;
{
 socket->_sockfd = 0;
 socket->_status = 0;
 socket->_ssl_ctx = NULL;
 socket->_ssl = NULL;
 socket->last_read = 0;
 socket->last_write = 0;
 memset( &socket->_their_addr, '\0', sizeof(socket->_their_addr));
}

bool _share_version_check(major, minor, subversion, builddate)
unsigned int major, minor, subversion, builddate;
{
 char _error_message[255];
 sprintf(_error_message, "Version is off, current: %d.%d.%d-%d | requires: %d.%d.%d-%d", SHARE_MAJ, SHARE_MIN, SHARE_SUB, SHARE_BUILD, major, minor, subversion, builddate);

 if( major > SHARE_MAJ ) {
  gerr(_error_message);
  return false;
 } else if( major < SHARE_MAJ ) {
  return true;
 }

 if( minor > SHARE_MIN ) {
  gerr(_error_message);
  return false;
 } else if( minor < SHARE_MIN ) {
  return true;
 }

 if( subversion > SHARE_SUB ) {
  gerr(_error_message);
  return false;
 } else if( subversion < SHARE_SUB ) {
  return true;
 }
 
 if( builddate > SHARE_BUILD ) {
  gerr(_error_message);
  return false;
 } else if( builddate < SHARE_BUILD ) {
  return true;
 }

 return true;
}

void share_make_pthread(socket)
SOCK *socket;
{
 socket->_status += STATUS_PTHREAD;
}
